#include "shell.h"
#include "completion.h"

void Ini_global_var();

void Dis_prompt();

void Read_cmd(FILE* script_name);
 
int Split_cmd();
 
int Split_bui_cmd(int count, int index);
 
void Execute_cmd();



 
char userinput[MAX_USERINPUT_SIZE];
 
char* cmds[MAX_cmd_NUM];
 
int cmd_count;
 
int left_count;
 
int right_count;
 
int Is_pipe;
 
int Is_in_redirect;
 
int Is_out_redirect_cov;
 
int Is_out_redirect_app;
 
int Is_bg;
 
char infile_path[MAX_PATH_LENGTH];
char outfile_path[MAX_PATH_LENGTH];

 
job* jobs;
int* job_count;
int job_shmID;

 
struct sigaction old_action;
struct sigaction new_action;

const char* history_file = "/home/rcy/m_shell/history.txt";

void Initial() {
    rl_initialize();
    rl_attempted_completion_function = completion;
    rl_bind_key('\t', rl_complete);
     
    
      
    struct stat st;
    if (stat(history_file, &st) == -1) {
        FILE* file = fopen(history_file, "w");
        if (file) {
            fclose(file);
        } else {
            fprintf(stderr, "Failed to create history file.\n");
            return 1;
        }
    }
    read_history(history_file);
}

 
int main(int argc, char **argv)
{
  
   
  int script_flag = 0; 
    
  FILE* fp;
   
  Init_joblist();
   
  Init_signal();
   
  setenv("SHELL","/home/lx/myshell",1);
  
   
  Initial();
  
   
  while(1){
     
    Ini_global_var();
     
    if(argc == 1)
      Dis_prompt();
     
    if(argc == 1)
      Read_cmd(NULL);
    else{
       
      if(!script_flag){
         
        script_flag = 1;
         
        if(!(fp = fopen(argv[1], "r"))){
          fprintf(stderr, RED "[myshell] Error: Cannot open the script file named \"%s\"!\n", argv[1]);
          exit(1);
        }      
      }
      
      else
        Read_cmd(fp);     
    }
      
     
    int flag = Split_cmd();
     
    if(flag == 1)
      continue;
     
    else if(flag == 2){
      fprintf(stderr, RED "[myshell] Error: Parameters are too much!\n");
      continue;
    }
     
    else
    Execute_cmd();
    write_history(history_file);
  }
  return 0;
}

 
void Ini_global_var(){
  cmd_count = 0;
  left_count = 0;
  right_count = 0;
  Is_pipe = 0;
  Is_in_redirect = 0;
  Is_out_redirect_cov = 0;
  Is_out_redirect_app = 0;
  Is_bg = 0;
  strcpy(infile_path,"\0");
  strcpy(outfile_path,"\0");
}



 
void Dis_prompt()
{
  char path[MAX_PATH_LENGTH];
  char username[MAX_USERNAME_LENGTH];
  char hostname[MAX_HOSTNAME_LENGTH];
   
  getcwd(path,MAX_PATH_LENGTH);
   
  struct passwd *pwd;
   
  pwd = getpwuid(getuid());
   
  strcpy(username,pwd->pw_name);
   
  gethostname(hostname,MAX_HOSTNAME_LENGTH);
   
  printf(YELLOW "[myshell]%s@%s", username,hostname);
  printf(WHITE ":");
  printf(CYAN "%s", path);
  printf(WHITE "$ ");
}

  
void Read_cmd(FILE* script_fp) {
      
    if (!script_fp) {
          
        char* input = readline("");
        if (input) {
            strncpy(userinput, input, MAX_USERINPUT_SIZE - 1);
            userinput[MAX_USERINPUT_SIZE - 1] = '\0';
            add_history(input);
            
         
                     
    FILE *alias_file = fopen("/home/rcy/m_shell/alias.txt", "r");
    if (alias_file == NULL) {
        fprintf(stderr, RED "[myshell] Error: Failed to open alias file!\n");
        return;
    }
    
    char alias_line[MAX_USERINPUT_SIZE];
    while (fgets(alias_line, sizeof(alias_line), alias_file) != NULL) {
        char alias_cmd[MAX_USERINPUT_SIZE];
        char alias_value[MAX_USERINPUT_SIZE];
        sscanf(alias_line, "%s %[^\n]", alias_cmd, alias_value);
        
        if (strstr(userinput, alias_cmd) != NULL) {
            char* replaced_cmd = strstr(userinput, alias_cmd);
            strcpy(replaced_cmd, alias_value);
        }
    }
    fclose(alias_file);
         
            
            
            
if (strstr(userinput, "!!") != NULL) {
    char last_cmd[MAX_USERINPUT_SIZE];
    FILE *history_file = fopen("history.txt", "r");
    if (history_file == NULL) {
        fprintf(stderr, RED "[myshell] Error: Failed to open history file!\n");
        return;
    }
    
     
    char current_line[MAX_USERINPUT_SIZE];
    char last_line[MAX_USERINPUT_SIZE];
    last_line[0] = '\0';
    while (fgets(current_line, sizeof(current_line), history_file) != NULL) {
        if (strcmp(current_line, "!!\n") != 0) {
            strcpy(last_line, current_line);
        }
    }
    fclose(history_file);
    
     
    if (last_line[0] != '\0') {
        strcpy(last_cmd, last_line);
    } else {
        fprintf(stderr, RED "[myshell] Error: No previous command found in history!\n");
        return;
    }
    
     
    char* replaced_cmd = strstr(userinput, "!!");
    strcpy(replaced_cmd, last_cmd);
}


    
    if (strstr(userinput, "!") != NULL && strstr(userinput, "!?") == NULL) {  
        int number;
        char cmd_offset[MAX_USERINPUT_SIZE];
        if (sscanf(userinput, "!%d%s", &number, cmd_offset) == 1) { 
            FILE *history_file = fopen("history.txt", "r");
            if (history_file == NULL) {
                fprintf(stderr, RED "[myshell] Error: Failed to open history file!\n");
                return;
            }
            char line[MAX_USERINPUT_SIZE];
            int current_line = 0;
            while (fgets(line, sizeof(line), history_file) != NULL) {
                if (++current_line == number) {
                    strcpy(userinput, line);
                    break;
                }
            }
            fclose(history_file);
        }
    }

    
    if (strstr(userinput, "!?") != NULL) {
        char *start_ptr = strchr(userinput, '?') + 1;
        char *end_ptr = strrchr(userinput, '?');
        if (start_ptr == NULL || end_ptr == NULL || start_ptr >= end_ptr) {
            fprintf(stderr, RED "[myshell] Error: Invalid cmd format\n");
            return;
        }
        char match_str[MAX_USERINPUT_SIZE] = {0};
        strncpy(match_str, start_ptr, end_ptr - start_ptr);

        FILE *history_file = fopen("history.txt", "r");
        if (history_file == NULL) {
            fprintf(stderr, RED "[myshell] Error: Failed to open history file!\n");
            return;
        }
        char line[MAX_USERINPUT_SIZE];
        char result[MAX_USERINPUT_SIZE] = {0};

        while (fgets(line, sizeof(line), history_file) != NULL) {
            if (strstr(line, match_str) != NULL) {
                strncat(result, line, strlen(line)-1);  
                strncat(result, " ", 1);
            }
        }
        fclose(history_file);
        result[strlen(result) - 1] = '\0'; 
        strcpy(userinput, result);
    }


            free(input);
        }
    } else {
        if (fgets(userinput, MAX_USERINPUT_SIZE - 1, script_fp) == NULL) {
            fclose(script_fp);
            exit(0);
        }
    }
}
 
int Split_cmd()
{ 
   
  for(int i=0; i<MAX_cmd_NUM; i++)
    cmds[i] = NULL;
  cmd_count = 0;
  
   
  int need_to_cop_infile = 0;  
  int need_to_cop_outfile = 0;  
   
  char* temp;
   
  char* sep_signal = " \n\t";
   
  temp = strtok(userinput,sep_signal);
   
  if(!temp)
    return 1;
  cmds[0] = temp;
  cmd_count++;
 
   
  while(cmd_count < MAX_cmd_NUM && (cmds[cmd_count] = strtok(NULL,sep_signal)) ){
    
     
    if(Is_pipe){
      cmd_count++;
      right_count++;
      continue;
    }
    
     
    if(need_to_cop_infile){
      strcpy(infile_path,cmds[cmd_count]);
      need_to_cop_infile = 0;
      continue;
    }
    
         
    if(need_to_cop_outfile){
      strcpy(outfile_path,cmds[cmd_count]);
      need_to_cop_outfile = 0;
      continue;
    }   
    
     
    if(strcmp(cmds[cmd_count],"<") == 0){
      need_to_cop_infile = 1;
      Is_in_redirect = 1;
      continue;
    }
    
       
    if(strcmp(cmds[cmd_count],">") == 0){
      need_to_cop_outfile = 1;
      Is_out_redirect_cov = 1;
      continue;
    }
    
    if(strcmp(cmds[cmd_count],">>") == 0){
      need_to_cop_outfile = 1;
      Is_out_redirect_app = 1;
      continue;
    }

    if(strcmp(cmds[cmd_count],"|") == 0){
      Is_pipe = 1;
      left_count = cmd_count;
      continue;
    }
    
     
    cmd_count++;  
  }
    
   
  if(cmd_count >= MAX_cmd_NUM)
    return 2;
    
   
  if(strcmp(cmds[cmd_count - 1],"&") == 0){
    Is_bg = 1;
     
    cmds[cmd_count - 1] = NULL;
    cmd_count--;
  }
    
   
  return 0;
  
}

 
int Split_bui_cmd(int count, int index)
{  
   
  if(strcmp(cmds[index],"clr") == 0)
    Clear_func();
    
  else if(strcmp(cmds[index],"environ") == 0)
    Env_func();
    
  else if(strcmp(cmds[index],"pwd") == 0)
    Pwd_func();
    
  else if(strcmp(cmds[index],"exit") == 0)
    Exit_func();
    
  else if(strcmp(cmds[index],"time") == 0)
    Time_func(); 
       
  else if(strcmp(cmds[index],"umask") == 0)
    Umask_func(count,index); 
    
  else if(strcmp(cmds[index],"cd") == 0)
    Cd_func(count,index); 
      
  else if(strcmp(cmds[index],"dir") == 0)
    Dir_func(count,index); 
    
  else if(strcmp(cmds[index],"echo") == 0)    
    Echo_func(count,index); 
    
  else if(strcmp(cmds[index],"exec") == 0)   
    Exec_func(count,index); 
    
  else if(strcmp(cmds[index],"set") == 0)     
    Set_func(count,index); 
    
  else if(strcmp(cmds[index],"unset") == 0)    
    Unset_func(count,index); 
    
  else if(strcmp(cmds[index],"test") == 0)  
    Test_func(count,index); 
    
  else if(strcmp(cmds[index],"shift") == 0)   
    Shift_func(count,index); 
    
  else if(strcmp(cmds[index],"jobs") == 0)     
    Jobs_func();
    
  else if(strcmp(cmds[index],"fg") == 0)    
    To_fg_func();
    
  else if(strcmp(cmds[index],"bg") == 0)      
    To_bg_func();

  else if(strcmp(cmds[index],"history") == 0)      
    History_func(count,index);
      
  else if(strcmp(cmds[index],"alias") == 0)      
    Alias_func(count,index);
  else if (strcmp(cmds[index], "unalias") == 0)
    UnAlias_func(count, index);
  else if(strcmp(cmds[index],"help") == 0)
    Help_func();
  
   
  else
    return 1;
   
  return 0;
}

 
void Execute_cmd()
{
   
  int stdinFd = dup(fileno(stdin));
  int stdoutFd = dup(fileno(stdout));
  
   
  if(strcmp(cmds[0],"quit") == 0){
       
      printf(CYAN "[myshell] Thanks for your using!\n");
      Free_joblist();
      exit(0);  
  }
  
   
  if(strcmp(cmds[0],"help") == 0){
     
    if(!Is_out_redirect_cov && !Is_out_redirect_app){
       
      Is_pipe = 1;
       
      left_count = 1;
      right_count = 1;
       
      cmds[1] = "more";    
    }
     
    else;
  }
  
   
  if(Is_pipe){
     
    int pipeFd[2];
    int status;
     
    if(pipe(pipeFd) == -1){
      fprintf(stderr, RED "[myshell] Error: pipe() is failed!\n");
      exit(1);
    }
    
     
    pid_t pid1 = fork();
    
     
    if(pid1 == 0){
       
      dup2(pipeFd[1], 1);
       
      close(pipeFd[0]);
       
      if(Split_bui_cmd(left_count,0) == 1){
       
      char* left_cmds[left_count + 1];
      for(int i=0; i<left_count; i++)
        left_cmds[i] = cmds[i]; 
       
      left_cmds[left_count] = NULL;
         
        if(execvp(left_cmds[0],left_cmds)!=0)
          fprintf(stderr, RED "[myshell] Error: Can not find the cmd named \"%s\"!\n", cmds[0]);      
      }
       
      exit(EXIT_SUCCESS);        
    }
    
     
    else if(pid1 > 0){
       
       
      waitpid(pid1,&status,0);
      
       
      pid_t pid2 = fork();
      
       
      if(pid2 == -1)
        fprintf(stderr, RED "[myshell] Error: fork() is failed!\n"); 
        
       
      else if(pid2 == 0){
         
        close(pipeFd[1]);
         
        dup2(pipeFd[0], 0);
               
        if(Split_bui_cmd(right_count,left_count) == 1){
           
          char* right_cmds[right_count + 1];
          for(int i=0; i<right_count; i++)
            right_cmds[i] = cmds[left_count + i]; 
           
          right_cmds[right_count] = NULL;
           
          if(execvp(right_cmds[0],right_cmds)!=0)
            fprintf(stderr, RED "[myshell] Error: Can not find the cmd named \"%s\"!\n", right_cmds[0]);      
        }
         
        exit(EXIT_SUCCESS);       
      }
      
       
      else{
         
        close(pipeFd[0]);
        close(pipeFd[1]);

        waitpid(pid2,&status,0);
         
      }  

    }
    
     
    else{
      fprintf(stderr, RED "[myshell] Error: fork() is failed!\n"); 
      exit(1);
    }

    return;
  }
  
   
  if(Is_in_redirect){
    int fileFd = 0;
     
    fileFd = open(infile_path, O_RDONLY, 0666);
     
    if(dup2(fileFd, fileno(stdin)) == -1)
      fprintf(stderr,RED "[myshell] Error: dup2() is failed!\n");
     
    close(fileFd);
  }
  
   
  if(Is_out_redirect_cov){
    int fileFd = 0;
     
    fileFd = open(outfile_path, O_RDWR | O_CREAT | O_TRUNC, 0666);
     
    if(dup2(fileFd, fileno(stdout)) == -1)
      fprintf(stderr,RED "[myshell] Error: dup2() is failed!\n");
     
    close(fileFd);
  }
  
   
  if(Is_out_redirect_app){
    int fileFd = 0;
     
    fileFd = open(outfile_path, O_RDWR | O_CREAT | O_APPEND, 0666);
     
    if(dup2(fileFd, fileno(stdout)) == -1)
      fprintf(stderr,RED "[myshell] Error: dup2() is failed!\n");
     
    close(fileFd);
  }
  
  if(Split_bui_cmd(cmd_count,0) == 1){
      
     
      pid_t pid = fork();
      
      
      if(!pid){
         
        Add_environ_var("PARENT=/home/lx/myshell");
               
        if(execvp(cmds[0],cmds)!=0)
          fprintf(stderr, RED "[myshell] Error: Can not find the cmd named \"%s\"!\n", cmds[0]);
         
        exit(EXIT_SUCCESS);
      }
      
       
      else if(pid > 0){
        int status;
         
        if(!Is_bg){
           
          Add_job(pid, cmds[0], FG, RUN);   
           
          waitpid(pid,&status,WUNTRACED);         
        }
        
         
        else{
           
          Add_job(pid, cmds[0], BG, RUN);
           
          waitpid(pid,&status,WNOHANG);
          int count = *job_count;
          int bg_cnt = 0;
          for(int i=0;i<count;i++)
            if(jobs[i].type == BG)
              bg_cnt++;
           
          printf("[%d] %d\n",bg_cnt,pid);             
        }

      }
      
       
      else
        fprintf(stderr, RED "[myshell] Error: fork() is failed!\n");     
  }
  
   
  if(Is_in_redirect){
     
    if(dup2(stdinFd, fileno(stdin)) == -1)
      fprintf(stderr,RED "[myshell] Error: dup2() is failed!\n");
     
    close(stdinFd);
  }  
  
   
  if(Is_out_redirect_cov || Is_out_redirect_app){
    
    if(dup2(stdoutFd, fileno(stdout)) == -1)
      fprintf(stderr,RED "[myshell] Error: dup2() is failed!\n");
     
    close(stdoutFd);
  }  

}


